Understanding the Servlet Life Cycle in Advanced Java
Course: Advanced Java Training
Topic: Understanding the Servlet Life Cycle
Course Link: Advanced Java Training Course
Demo Link: Register for a Course Demo
Website: JustAcademy
1. Introduction to the Servlet Life Cycle
A servlet is a Java class that runs inside a web container and handles requests from clients, usually through HTTP. Servlets are commonly used to build dynamic web applications, process form submissions, validate user input, communicate with databases, and generate responses.
The Servlet Life Cycle describes the stages through which a servlet passes from creation to removal. The servlet container, such as Apache Tomcat, manages these stages and calls the appropriate life-cycle methods.
The three main life-cycle methods defined by the Servlet API are:
init() — initializes the servlet.
service() — processes client requests.
destroy() — performs cleanup before the servlet is removed from service.
Before these methods are used, the container loads the servlet class and creates an instance. After the servlet is destroyed, the object may eventually become eligible for garbage collection.
2. Learning Objectives
- Understand the purpose of the servlet life cycle.
- Learn how a servlet is loaded and instantiated.
- Understand the roles of
init(), service(), and destroy().
- Differentiate between
GenericServlet and HttpServlet.
- Learn how HTTP requests are handled through
doGet() and doPost().
- Understand servlet initialization parameters and configuration.
- Explore servlet threading, resource management, and best practices.
- Build and test a basic servlet application.
3. What Is a Servlet Container?
A servlet container is a component of a web server or application server that manages servlet execution. Apache Tomcat is a commonly used servlet container.
The container is responsible for loading servlet classes, creating servlet instances, initializing servlets, mapping requests to the appropriate servlet, providing request and response objects, managing concurrent requests, and invoking destruction when a servlet is taken out of service.
Responsibilities of a Servlet Container
| Responsibility |
Description |
| Class loading |
Loads the servlet class when needed. |
| Instantiation |
Creates the servlet object. |
| Initialization |
Calls init() before the servlet handles requests. |
| Request handling |
Invokes the servlet's request-processing methods. |
| Configuration |
Provides servlet configuration and application context information. |
| Concurrency management |
Coordinates requests, which may be processed concurrently. |
| Cleanup |
Calls destroy() when taking the servlet out of service. |
4. Stages of the Servlet Life Cycle
The servlet life cycle can be understood through the following sequence.
Client sends an HTTP request
|
v
Servlet Container receives the request
|
v
Load Servlet Class (when required)
|
v
Create Servlet Instance
|
v
Call init() once after successful instantiation
|
v
Call service() to process requests
|
v
HttpServlet dispatches to doGet(), doPost(), etc.
|
v
Generate and return HTTP response
|
v
Repeat request processing as needed
|
v
Call destroy() when servlet is taken out of service
|
v
Object may become eligible for garbage collection
Important: The container may load a servlet at application startup or delay loading until the servlet is first needed. The life cycle is managed by the container, not directly by application code.
5. Loading and Instantiation
The first stage involves loading the servlet class and creating its object. The container locates the compiled servlet class, loads it using Java class-loading mechanisms, and instantiates it.
Example Servlet Class
import jakarta.servlet.http.HttpServlet;
public class WelcomeServlet extends HttpServlet {
// Servlet implementation
}
When the container needs to activate this servlet, it creates an instance of WelcomeServlet. The container then initializes the instance before delivering requests to it.
Key Points
- Servlet instances are managed by the container.
- Loading may occur during application startup or when the servlet is first requested.
- The constructor runs when the instance is created.
- Successful initialization must occur before the servlet handles requests.
- Developers should not manually create servlet instances to handle ordinary web requests.
6. The init() Method
The init() method is called by the servlet container to initialize a servlet. It runs after the servlet instance has been created and before that instance begins processing requests.
Initialization is intended for one-time setup, such as reading servlet-specific configuration, preparing reusable objects, or validating application configuration.
Syntax
@Override
public void init() throws ServletException {
// Perform one-time initialization
}
The Servlet API also provides an initialization form that receives a ServletConfig object. In HttpServlet, developers can override the no-argument init() method for custom setup while allowing the inherited implementation to manage the normal configuration initialization.
Example: Using init()
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/welcome")
public class WelcomeServlet extends HttpServlet {
private String applicationMessage;
@Override
public void init() throws ServletException {
applicationMessage = "Welcome to Advanced Java!";
log("WelcomeServlet initialized.");
}
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws IOException {
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().println(applicationMessage);
}
}
Explanation
- The container loads and creates the
WelcomeServlet instance.
- The container initializes the servlet.
- The custom
init() method assigns a message to the instance field.
- The container makes the successfully initialized servlet available to process requests.
- Each matching GET request can use the initialized message.
Important Characteristics of init()
- It is called once for a successfully initialized servlet instance.
- It executes before the instance handles client requests.
- It can throw
ServletException if initialization fails.
- It should not perform unnecessary, slow operations that delay application startup or activation.
- Initialization failures prevent that instance from entering normal service.
7. The service() Method
The service() method is responsible for handling incoming requests. The servlet container invokes it with request and response objects for each request routed to the servlet.
For an HTTP servlet, HttpServlet implements HTTP-specific dispatching. Its service() method examines the HTTP method and routes the request to a suitable method, such as doGet(), doPost(), doPut(), or doDelete().
General Syntax
public void service(
ServletRequest request,
ServletResponse response)
throws ServletException, IOException {
// Process the request
}
When extending HttpServlet, developers usually override the relevant doXxx() method instead of overriding service() directly.
How service() Works
- The client sends an HTTP request to the web server.
- The container identifies the target servlet using its URL mapping.
- The container supplies request and response objects.
- The servlet's service mechanism determines how to handle the request.
- For an HTTP servlet, the appropriate HTTP method handler is called.
- The servlet writes the response body, headers, or status as required.
- The container sends the response back to the client.
8. HTTP Request-Handling Methods
The HttpServlet class provides methods for handling different HTTP request types.
| Method |
Typical Purpose |
doGet() |
Handles GET requests, commonly used to retrieve or display information. |
doPost() |
Handles POST requests, commonly used to submit data or initiate an operation. |
doPut() |
Handles PUT requests, commonly used to create or replace a resource representation. |
doDelete() |
Handles DELETE requests, commonly used to request resource deletion. |
doHead() |
Handles HEAD requests, which return headers without a response body. |
doOptions() |
Handles OPTIONS requests, which describe supported communication options. |
doTrace() |
Handles TRACE requests when supported and enabled by the server configuration. |
The correct method depends on the HTTP operation and application design. A POST request is not automatically secure simply because its data is not normally placed in the URL; HTTPS and proper authentication, authorization, and input validation are still essential.
9. Example: Handling GET and POST Requests
import java.io.IOException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws IOException {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().println(
"<h2>Submit your message</h2>" +
"<form method='post' action='message'>" +
"<input type='text' name='message' required>" +
"<button type='submit'>Send</button>" +
"</form>"
);
}
@Override
protected void doPost(
HttpServletRequest request,
HttpServletResponse response)
throws IOException {
request.setCharacterEncoding("UTF-8");
String message = request.getParameter("message");
response.setContentType("text/plain;charset=UTF-8");
if (message == null || message.isBlank()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().println("Message is required.");
return;
}
response.getWriter().println("Message received.");
}
}
Explanation
- The servlet is mapped to the
/message URL.
- A GET request displays a simple HTML form.
- Submitting the form sends a POST request.
- The servlet retrieves the submitted parameter using
getParameter().
- The application checks that the input is present before continuing.
- The servlet returns an appropriate response.
In a production application, escape untrusted data before inserting it into HTML, validate input according to business rules, and apply the application's authentication and authorization requirements.
10. The destroy() Method
The destroy() method is called by the container when a servlet is being taken out of service. It provides an opportunity to release resources and perform final cleanup.
Syntax
@Override
public void destroy() {
// Release resources owned by the servlet
}
Example: Using destroy()
import java.io.IOException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/status")
public class StatusServlet extends HttpServlet {
private String statusMessage;
@Override
public void init() {
statusMessage = "Application is running.";
log("StatusServlet initialized.");
}
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws IOException {
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().println(statusMessage);
}
@Override
public void destroy() {
log("StatusServlet is being taken out of service.");
statusMessage = null;
}
}
This example demonstrates the cleanup hook. Setting a field to null is not normally necessary for simple strings, but the method is useful when a servlet owns resources that require explicit shutdown.
Typical Cleanup Activities
- Shutting down an executor service created and owned by the servlet.
- Closing a resource that the servlet itself owns and is responsible for closing.
- Stopping background tasks started by the servlet.
- Flushing or releasing application-managed resources when appropriate.
- Removing references to resources that should no longer be retained.
Important: A servlet should not close shared resources owned by the container or application framework. For example, a shared connection pool should be closed by the component that owns it, not by an individual servlet that merely borrows connections from it.
11. Difference Between init(), service(), and destroy()
| Feature |
init() |
service() |
destroy() |
| Purpose |
Initialize servlet state. |
Process client requests. |
Clean up before removal. |
| Invocation |
Once per successfully initialized instance. |
For each request handled by the servlet. |
When the instance is taken out of service. |
| Timing |
After instantiation, before requests are processed. |
After initialization and during normal operation. |
During shutdown or removal from service. |
| Typical work |
Read configuration and prepare resources. |
Validate input and produce a response. |
Stop owned tasks and release owned resources. |
| Method signature |
init() or init(ServletConfig) |
service(request, response) |
destroy() |
| Frequency |
One successful initialization per instance. |
Repeated as requests arrive. |
Normally once when the instance is removed from service. |
12. ServletConfig and Initialization Parameters
ServletConfig provides configuration information for an individual servlet. It can be used to read initialization parameters supplied through annotations or deployment configuration.
Example Using @WebInitParam
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet(
urlPatterns = "/greeting",
initParams = {
@WebInitParam(
name = "greetingMessage",
value = "Welcome to Java Servlets!"
)
}
)
public class GreetingServlet extends HttpServlet {
private String greetingMessage;
@Override
public void init() throws ServletException {
greetingMessage = getServletConfig()
.getInitParameter("greetingMessage");
if (greetingMessage == null) {
throw new ServletException(
"Required greetingMessage parameter is missing."
);
}
}
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws IOException {
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().println(greetingMessage);
}
}
Explanation
@WebServlet defines the URL mapping and servlet initialization parameter.
init() reads the configured value using getServletConfig().
- The value is stored for use during subsequent requests.
- If the required parameter is missing, initialization fails with a
ServletException.
ServletConfig vs ServletContext: ServletConfig provides configuration for a particular servlet, while ServletContext provides application-wide information and resources shared across the web application.
13. Servlet Life Cycle and Multithreading
Servlet containers commonly handle multiple requests concurrently. In a typical servlet model, the container uses the same servlet instance to process multiple requests using different threads.
This means that instance fields may be accessed by more than one request at the same time. Developers must avoid storing request-specific data in shared servlet fields.
Unsafe Example
public class UnsafeServlet extends HttpServlet {
private String currentUsername;
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response) {
currentUsername = request.getParameter("username");
// Concurrent requests may overwrite currentUsername.
}
}
Safer Approach
public class SafeServlet extends HttpServlet {
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws IOException {
String username = request.getParameter("username");
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().println(
username == null ? "No username supplied." : "Request received."
);
}
}
In the safer version, the username is stored in a local variable associated with the current request. Local variables are not shared between request-processing threads, although objects referenced by those variables may still be shared if they come from shared application state.
Multithreading Best Practices
- Use local variables for request-specific information.
- Avoid mutable shared instance fields unless access is properly coordinated.
- Do not start unmanaged background threads for every request.
- Use thread-safe services and libraries when shared state is necessary.
- Keep request-processing operations efficient.
- Follow the container or framework's resource-management rules.
14. Servlet Life Cycle with Database Operations
A servlet may interact with a database as part of request processing. Database operations should use appropriate resource-management techniques and should not unnecessarily hold a database connection for the entire lifetime of the servlet.
Example: Retrieving a User by ID
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/user")
public class UserServlet extends HttpServlet {
private static final String URL =
"jdbc:mysql://localhost:3306/company";
private static final String DB_USER = "root";
private static final String DB_PASSWORD = "your_password";
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws IOException {
response.setContentType("text/plain;charset=UTF-8");
String idValue = request.getParameter("id");
if (idValue == null) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().println("User ID is required.");
return;
}
final int id;
try {
id = Integer.parseInt(idValue);
} catch (NumberFormatException e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().println("User ID must be a number.");
return;
}
String sql = "SELECT id, name FROM employees WHERE id = ?";
try (Connection con =
DriverManager.getConnection(URL, DB_USER, DB_PASSWORD);
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, id);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
response.getWriter().println(
"ID: " + rs.getInt("id") +
", Name: " + rs.getString("name")
);
} else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.getWriter().println("User not found.");
}
}
} catch (SQLException e) {
log("Database request failed.", e);
response.setStatus(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR
);
response.getWriter().println(
"Unable to process the request right now."
);
}
}
}
Note: This example uses placeholders for database credentials and assumes the JDBC driver and database table are configured. In production, store credentials securely and normally obtain connections from a managed connection pool or data source.
15. Servlet Life Cycle Events and Listeners
Servlet applications can also use listener interfaces to respond to web-application or session events. These listeners complement the servlet's own life cycle methods.
| Listener |
Purpose |
ServletContextListener |
Responds to web-application context initialization and destruction. |
ServletContextAttributeListener |
Observes changes to application context attributes. |
HttpSessionListener |
Responds to HTTP session creation and destruction. |
HttpSessionAttributeListener |
Observes changes to session attributes. |
ServletRequestListener |
Observes the beginning and end of servlet request processing. |
Listeners are useful for application-wide lifecycle events, while init(), service(), and destroy() control the lifecycle of a servlet instance.
16. Common Problems and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Servlet returns 404 |
Incorrect URL mapping or deployment context path. |
Verify @WebServlet, deployment configuration, and requested URL. |
| Servlet initialization fails |
Invalid configuration or an exception thrown during init(). |
Check server logs and verify initialization parameters. |
| Servlet class not found |
Incorrect package, build output, or deployment configuration. |
Verify the compiled class and application artifact. |
| HTTP 405 response |
The requested HTTP method is not supported by the servlet mapping or implementation. |
Implement the appropriate handler and verify the HTTP method. |
| Response is garbled |
Character encoding is missing or inconsistent. |
Set the response content type and character encoding correctly. |
| Incorrect data between users |
Request-specific state is stored in shared servlet fields. |
Use local variables and properly coordinate shared state. |
| Database connections are exhausted |
Connections are not closed or pool configuration is unsuitable. |
Use try-with-resources and review connection-pool metrics. |
| Cleanup does not run as expected |
Application termination was abrupt or container shutdown was interrupted. |
Implement graceful shutdown and do not rely on destroy() for crash recovery. |
17. Best Practices for Servlet Development
- Override only the lifecycle and request-handling methods needed by the application.
- Use
init() for lightweight, one-time initialization.
- Use
doGet(), doPost(), and other HTTP handlers according to the request's purpose.
- Use
destroy() to clean up resources owned by the servlet.
- Do not assume a servlet instance is created separately for every request.
- Avoid storing user-specific information in mutable instance fields.
- Use try-with-resources for database and file resources where appropriate.
- Validate input and encode output to help prevent injection and cross-site scripting vulnerabilities.
- Do not expose stack traces, credentials, or internal server details in responses.
- Use a supported Servlet API version and matching container version.
- Keep long-running operations and unmanaged background tasks out of request-handling code where possible.
- Test initialization failures, concurrent requests, and graceful shutdown behavior.
18. Practical Exercise
Task: Build a servlet-based Student Information application that demonstrates the servlet life cycle.
Requirements
- Create a servlet named
StudentServlet.
- Map the servlet to
/students.
- Override
init() to read an application message from an initialization parameter.
- Implement
doGet() to display the application message.
- Implement
doPost() to validate a submitted student name.
- Return a suitable response for missing or invalid input.
- Override
destroy() to log that the servlet is being taken out of service.
- Deploy the application to a compatible servlet container and test it in a browser.
Expected Learning Outcome
After completing the exercise, you should be able to explain when the container initializes a servlet, how requests are routed to HTTP handlers, how configuration parameters are read, and how cleanup is performed when a servlet is removed from service.
19. Interview Questions and Answers
Q1. What is the servlet life cycle?
Answer: It is the sequence of stages managed by the servlet container, including loading and instantiation, initialization through init(), request processing through service(), and cleanup through destroy().
Q2. Who manages the servlet life cycle?
Answer: The servlet container, such as Apache Tomcat, manages the servlet's creation, initialization, request dispatching, and removal from service.
Q3. How many times is init() called?
Answer: It is called once for a successfully initialized servlet instance. If initialization fails, that instance does not enter normal service.
Q4. What is the purpose of service()?
Answer: The method processes incoming requests. In HttpServlet, the service implementation dispatches HTTP requests to methods such as doGet() and doPost().
Q5. What is the purpose of destroy()?
Answer: It gives the container an opportunity to take a servlet out of service and allows the servlet to release resources it owns.
Q6. What is the difference between GenericServlet and HttpServlet?
Answer: GenericServlet provides a protocol-independent base implementation of the Servlet API, while HttpServlet extends it with HTTP-specific request handling.
Q7. Can a servlet handle multiple requests at the same time?
Answer: Yes. Containers commonly process concurrent requests using multiple threads. Developers must ensure that shared mutable state is handled safely.
Q8. What happens if init() throws ServletException?
Answer: The container does not place that servlet instance into normal service. The container may attempt to create and initialize another instance according to its behavior and configuration.
Q9. Is destroy() called after every request?
Answer: No. It is called when the servlet instance is taken out of service, not after each request.
Q10. What is the difference between ServletConfig and ServletContext?
Answer: ServletConfig contains configuration for a particular servlet, whereas ServletContext represents the web application's shared context.
Q11. Should database connections be opened in init() and kept for the servlet's entire life?
Answer: Usually not. A servlet should generally acquire a connection for the required operation and release it afterward, often through a data source or connection pool. Long-lived resources should be managed according to their ownership and concurrency requirements.
Q12. When does a servlet become eligible for garbage collection?
Answer: After the object is no longer reachable, it may become eligible for garbage collection. Calling destroy() does not itself guarantee immediate garbage collection.
20. Quick Revision Notes
- Loading: The container loads the servlet class when needed.
- Instantiation: The container creates the servlet object.
- Initialization:
init() prepares the servlet for use.
- Request processing:
service() handles requests; HttpServlet dispatches to HTTP handlers.
- HTTP methods:
doGet(), doPost(), doPut(), and doDelete() handle common HTTP operations.
- Cleanup:
destroy() gives the servlet a chance to release resources before removal.
- Concurrency: Multiple requests may be processed concurrently, so avoid unsafe shared mutable state.
- Configuration: Use
ServletConfig for servlet-specific initialization parameters.
- Resource management: Close resources according to their ownership and lifecycle.
21. Conclusion
Understanding the servlet life cycle is essential for building reliable Java web applications. The servlet container manages the servlet from loading and initialization through request processing and eventual destruction.
By understanding init(), service(), and destroy(), developers can initialize components correctly, handle HTTP requests efficiently, manage resources responsibly, and build applications that behave predictably under concurrent workloads.
22. Additional Learning Resources